package controlador;

import java.util.Collections;
import java.util.List;
import java.util.Stack;

import eventos.EventoHojaDibujo;

import logica.Entidad;
import logica.EntidadRepetidaException;
import logica.IdExternoInexistenteException;
import logica.ObjetoDiagrama;

import memento.ControladorDeAcciones;
import memento.EstadoInicial;
import memento.IMemento;

import vista.objetos.DiagramaGraficable;
import vista.objetos.FabricaAtributoGraficable;
import vista.objetos.GraficableConEtiqueta;
import vista.objetos.GraficableConEtiquetaConAtributos;
import vista.objetos.JerarquiaGraficable;
import vista.objetos.NoEsObjetoDiagramableException;
import vista.objetos.NoEsObjetoNodoException;
import vista.objetos.ObjetoGraficableRepetidoException;
import vista.objetos.ProyectoGraficable;
import vista.paneles.TipoEvento;
import vista.ventanas.VentanaEditor;

public class ControladorEditor implements IControladorEntidadExistente, IControladorAtributo, IControladorJerarquia
{
	private ProyectoGraficable proyectoGraficableActual = null;
	private VentanaEditor ventanaEditor = null;
	private EstadoInicial estadoInicialDeDiagActual = new EstadoInicial();

	private final ControladorDeAcciones controladorDeAcciones = new ControladorDeAcciones();

	private GraficableConEtiqueta graficableEnBuffer;
	private Stack<IMemento> graficableEditando = new Stack<IMemento>();
	
	private static ControladorEditor controlador = new ControladorEditor();
	private ControladorEditor () {}
	
	public static ControladorEditor getInstancia(){
		return controlador;
	}
	
	public void abrirVentanaEditor(ProyectoGraficable proyectoGraficable)
	{
		this.proyectoGraficableActual = proyectoGraficable;
		this.ventanaEditor = new VentanaEditor(proyectoGraficableActual.getDiagramaPpal());
	
		this.agregarBotonesAVentana();
		this.ventanaEditor.getHojaDeDibujo().setManejadorDeEventos(new EventoHojaDibujo(this.ventanaEditor.getHojaDeDibujo(), this));
		
		this.estadoInicialDeDiagActual.setEstadoInicial(proyectoGraficableActual.getDiagramaActual().getGraficables());
	}
	
	private void agregarBotonesAVentana() 
	{
		InicializadorDeBotonesDeOpcion fabrica = new InicializadorDeBotonesDeOpcion();
		fabrica.escucharEventos(this.ventanaEditor, this);
		fabrica.agregarBotones(this.ventanaEditor);
	}

	@Override
	public void verificarSiSePuedeAgregarObjeto(ObjetoDiagrama objeto) throws EntidadRepetidaException, IdExternoInexistenteException {
		this.proyectoGraficableActual.getDiagramaActual().getDiagrama().verificarSiSePuedeAgregarObjeto(objeto);
	}

	@Override
	public void agregarObjetoExistenteAlModelo(GraficableConEtiqueta graficable) throws NoEsObjetoNodoException, ObjetoGraficableRepetidoException, NoEsObjetoDiagramableException 
	{
		DiagramaGraficable diagActual = this.proyectoGraficableActual.getDiagramaActual();
		
		diagActual.verificarSiGraficableRepetido(graficable);
		diagActual.agregarGraficable(graficable);
		agregarEventosAGraficable(graficable);
		this.ventanaEditor.agregarObjetoGraficableAlArbol(graficable);
	}

	@Override
	public void agregarObjetoAlModelo(GraficableConEtiqueta graficable) throws EntidadRepetidaException, IdExternoInexistenteException, NoEsObjetoDiagramableException, NoEsObjetoNodoException, ObjetoGraficableRepetidoException  
	{
		DiagramaGraficable diagActual = this.proyectoGraficableActual.getDiagramaActual();
		
		diagActual.verificarSiGraficableRepetido(graficable);
		diagActual.getDiagrama().agregarObjetoDiagrama(graficable.getObjetoDiagramaDecorado());
		diagActual.agregarGraficable(graficable);
		agregarEventosAGraficable(graficable);
		this.ventanaEditor.agregarObjetoGraficableAlArbol(graficable);
	}

	private void agregarEventosAGraficable(GraficableConEtiqueta graficable) throws NoEsObjetoNodoException 
	{
		// para que la etiqueta escuche eventos
		graficable.agregarEventoEtiqueta(this.ventanaEditor.getHojaDeDibujo());
		// sete el nodo q representa el graficable en el JTree
    	graficable.setNodo();
    	// seteo el evento editar del popupMenu del nodo
    	graficable.nodoEscucharEventoEditar(this.ventanaEditor);
    	// creo un popupMenu para la etiqueta que representa el graficable en la hoja
		graficable.setPopupMenu(); //para q aparezca el popup en la hoja de dibujo
    	// seteo el evento editar del popupMenu de la etiqueta del graficable
		graficable.escucharEventoEditar(this.ventanaEditor);
	}

	@Override
	public List<Entidad> getEntidadesExitentes()
	{	
		List<Entidad> listaEntidades = this.proyectoGraficableActual.getDiagramaActual().getDiagrama().getEntidades(); 
		Collections.sort(listaEntidades);
		return listaEntidades;
	}

	public ProyectoGraficable getProyectoActual() {
		return this.proyectoGraficableActual;
	}
		
	@Override
	public void dibujarGraficableFlotante(GraficableConEtiqueta grafFlotante, TipoEvento tipoEvento) {
		this.ventanaEditor.getHojaDeDibujo().setGraficableAAgregar(grafFlotante, tipoEvento);
	}
	
	@Override
	public void dibujarJerarquia(JerarquiaGraficable jerarquiaGraf) 
	{
		jerarquiaGraf.inicializarPosiciones();

		this.ventanaEditor.getHojaDeDibujo().setJerarquiaAAgregar(jerarquiaGraf);		
	}
	
	@Override
	public void actualizarVista() {
		this.ventanaEditor.actualizarVista();	
	}

	public synchronized GraficableConEtiqueta getGraficableEnBuffer() {
		return graficableEnBuffer;
	}

	public void pegarGraficableEnBuffer() {
		this.dibujarGraficableFlotante(this.getGraficableEnBuffer(), TipoEvento.AGREGAR_GRAFICABLE_EXISTENTE);
	}
	
	public void setGraficableEnBuffer(GraficableConEtiqueta graficableEnBuffer) {
		this.graficableEnBuffer = graficableEnBuffer;
	}
	
	public VentanaEditor getVentanaPrograma(){
		return this.ventanaEditor;
	}
	
	public EstadoInicial getEstadoInicialDeDiagActual() {
		return estadoInicialDeDiagActual;
	}
	
	public void setEstadoInicialDeDiagActual(EstadoInicial estadoInicialDeDiagActual) {
		this.estadoInicialDeDiagActual = estadoInicialDeDiagActual;
	}
	public ControladorDeAcciones getControladorDeAcciones() {
		return controladorDeAcciones;
	}

	@Override
	public void guardarMemento(IMemento memento) {
		this.getControladorDeAcciones().borrarObjetosParaRehacer();
		this.getControladorDeAcciones().guardarCambio(memento);		
	}

	@Override
	public void actualizarGraficableConAtributos(GraficableConEtiquetaConAtributos graf) {
		this.ventanaEditor.getHojaDeDibujo().actualizarGraficableConAtributo(graf);
		this.actualizarVista();		
	}

	public void cargarDiagrama(DiagramaGraficable diagrama) {
		this.getVentanaPrograma().cargarDiagrama(diagrama); // primero borro la vista anterior, carga la actual
		this.getProyectoActual().setDiagramaActual(diagrama); // actualizo la lógica
	}

	@Override
	public List<FabricaAtributoGraficable> getTipoDeAtributos() {
		return this.getProyectoActual().getTipoDeAtributos();
	}

	
	/*
	 * Para la edición de los objetos diagramables. Resuelve el tema del cancelar 
	 */
	@Override
	public IMemento getGraficableEditando() {
		return graficableEditando.peek();
	}

	@Override
	public void setGraficableEditando(IMemento graficableEditando) {
		this.graficableEditando.push(graficableEditando);
	}

	@Override
	public void sacarGraficableEditando() {
		this.graficableEditando.pop();		
	}

}